/**
1. `1 u`：子树$u$的每个节点的权值均进行异或，即01翻转；
2. `2 u d`：子树$u$的每个节点的权值均设置为$d,d\in\{0,1\}$；
3. `3 u v`：$u$到$v$的路径上的每个节点的权值均进行异或，即01翻转；
4. `4 u v d`：$u$到$v$的路径上的每个节点的权值均设置为$d,d\in\{0,1\}$；
5. `5 u v`：求$u$到$v$的路径上的每个节点的权值之和，并输出；
6. `6 u v`：求$u$到$v$的路径上的每个节点的权值中0的数量，并输出
7. `7 u v`：求$u$到$v$的路径上的最长连续1的数量，并输出；
8. `8 u v`：求$u$到$v$的路径上的最长连续0的数量，并输出；
9. `9 u v`：求$u$到$v$的路径上的连续的段数，并输出；
 * 
 */
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;

template <typename T>
const T & mymax(const T & a, const T & b){
    return max(a, b);
}

template<typename T, typename ... Args>
const T & mymax(const T & t, const Args & ... args){
    return max(t, mymax(args ...));
}

template<typename T>
void input(vector<T> & v, int n){
	v.assign(n + 1, {});
    for(int i=1;i<=n;++i) cin >> v[i];
	return;
}

template<typename T>
istream & operator >> (istream & is, vector<T> & v){
	for(auto & i : v) is >> i;
	return is;
}

using llt = long long;
using vi = vector<int>;
using vll = vector<llt>;


struct HLD{ // 重链剖分

using llt = long long;

struct _t{
    array<int, 2> longest; // 01最长的长度
    array<int, 2> left;    // 01左边长度
    array<int, 2> right;   // 01右边长度
    array<int, 2> count;   // 01数量
    int duan;              // 连续的段数 
    bool operator == (const _t & b) const {
        return longest == b.longest and left == b.left and right == b.right and count == b.count and duan == b.duan;
    }
};

using value_type = _t;
vector<value_type> data; // 线段树

using lazy_type = llt;
vector<lazy_type> lazy; // 延迟标记 按位操作, 210, 第2位表示异或，第1位表示设置，第0位表示设置的值

/// 从下往上计算信息，要变动
value_type _up_(const value_type & ls, const value_type & rs) {
    // assert(0);
    if(rs == value_zero()) return ls;
    if(ls == value_zero()) return rs;

    value_type ans;

    int leftn = ls.count[0] + ls.count[1]; // 左长度
    int rightn = rs.count[0] + rs.count[1]; // 右长度

    if(leftn == ls.right[0]){ // 左段全0
        assert(0 == ls.count[1] and 0 == ls.left[1] and 0 == ls.right[1] and 0 == ls.longest[1]);
        assert(leftn == ls.count[0] and leftn == ls.left[0] and leftn == ls.right[0] and leftn == ls.longest[0]);
        assert(1 == ls.duan);

        if(rightn == rs.left[0]){ // 右段全0
            ans = {
                {leftn + rightn, 0}, 
                {leftn + rightn, 0}, 
                {leftn + rightn, 0}, 
                {leftn + rightn, 0}, 
                1
            };
        }else if(rightn == rs.left[1]){
            ans = {
                {leftn, rightn}, 
                {leftn, 0}, 
                {0, rightn}, 
                {leftn, rightn}, 
                2
            };
        }else{
            int zero = leftn + rs.left[0];
            ans = {
                {max(zero, rs.longest[0]), rs.longest[1]}, 
                {zero, 0}, 
                rs.right, 
                {ls.count[0] + rs.count[0], rs.count[1]}, 
                zero == leftn ? rs.duan + 1 : rs.duan
            };
        }
    }else if(leftn == ls.right[1]){ // 左段全1
        assert(0 == ls.count[0] and 0 == ls.left[0] and 0 == ls.right[0] and 0 == ls.longest[0]);
        assert(leftn == ls.count[1] and leftn == ls.left[1] and leftn == ls.right[1] and leftn == ls.longest[1]);
        assert(1 == ls.duan);
    
        if(rightn == rs.left[0]){
            ans = {{rightn, leftn}, {0, leftn}, {rightn, 0}, {rightn, leftn}, 2};
        }else if(rightn == rs.left[1]){
            ans = {{0, leftn + rightn}, {0, leftn + rightn}, {0, leftn + rightn}, {0, leftn + rightn}, 1};
        }else{
            int one = leftn + rs.left[1];
            ans = {{rs.longest[0], max(one, rs.longest[1])}, {0, one}, rs.right, {rs.count[0], ls.count[1] + rs.count[1]}, one == leftn ? rs.duan + 1 : rs.duan};
        }
    }else{ // 左段都有
        assert(ls.count[0] and ls.count[1]);

        if(rightn == rs.left[0]){
            int zero = rightn + ls.right[0];
            ans = {{max(zero, ls.longest[0]), ls.longest[1]}, ls.left, {zero, 0}, {ls.count[0] + rs.count[0], ls.count[1]}, zero == rightn ? ls.duan + 1 : ls.duan};
        }else if(rightn == rs.left[1]){
            int one = rightn + ls.right[1];
            ans = {{ls.longest[0], max(ls.longest[1], one)}, ls.left, {0, one}, {ls.count[0], ls.count[1] + rs.count[1]}, one == rightn ? ls.duan + 1 : ls.duan};
        }else{
            int same0 = ls.right[0] and rs.left[0] ? 1 : 0;
            int same1 = ls.right[1] and rs.left[1] ? 1 : 0;
            assert(same0 + same1 < 2);
            ans = {{max(ls.longest[0], rs.longest[0]), max(ls.longest[1], rs.longest[1])}, ls.left, rs.right, {ls.count[0] + rs.count[0], ls.count[1] + rs.count[1]}, ls.duan + rs.duan - (same0 + same1)};
            if(same0) ans.longest[0] = max(ans.longest[0], ls.right[0] + rs.left[0]);
            else if(same1) ans.longest[1] = max(ans.longest[1], ls.right[1] + rs.left[1]);
        }
    }

    return ans;    
}

/// 从上往下计算信息，要变动
void _dn_(int t, int s, int e, const lazy_type & delta) {
    // assert(0);
    int n = e - s + 1;
    auto & x = data[t];
    /// 先设置
    if(delta & 2){
        int v = delta & 1;
        x.longest[v] = x.left[v] = x.right[v] = x.count[v] = n;
        x.longest[v ^ 1] = x.left[v ^ 1] = x.right[v ^ 1] = x.count[v ^ 1] = 0;
        x.duan = 1;
    }
    /// 再异或
    if(delta & 4){
        swap(x.longest[0], x.longest[1]);
        swap(x.left[0], x.left[1]);
        swap(x.right[0], x.right[1]);
        swap(x.count[0], x.count[1]);
    }

    /// 如果有设置操作
    if(delta & 2){
        lazy[t] = delta;
    }else{
        if((lazy[t] >> 2) == (delta >> 2)) lazy[t] &= 3;
        else lazy[t] |= 4;
    }
    return;
}

static value_type mkValue(llt data){
    // assert(0);
    if(data){
        return {array<int, 2>{0, 1}, array<int, 2>{0, 1}, array<int, 2>{0, 1}, array<int, 2>{0, 1}, 1};
    }
    return {array<int, 2>{1, 0}, array<int, 2>{1, 0}, array<int, 2>{1, 0}, array<int, 2>{1, 0}, 1};
}

/// 对于有顺序要求的值需要倒序操作
static void reverse(value_type & v){
    // assert(0);
    swap(v.left[0], v.right[0]);
    swap(v.left[1], v.right[1]);
}

/// 辅助函数，视延迟的类型而变动
static const lazy_type & lazy_zero() {
    static const lazy_type LAZY0 = 0;
    return LAZY0; 
}

/// 辅助函数，视线段树信息类型而变动
static const value_type & value_zero() {
    static const value_type VALUE0 {{0, 0}, {0, 0}, {0, 0}, {0, 0}, 0};
    return VALUE0;
}

/// 几乎不用动
value_type _query(int t, int s, int e, int a, int b) {
    if(a <= s and e <= b) {
        return data[t];
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    value_type ans = value_zero();
    if(a <= mid) ans = _up_(ans, _query(lson(t), s, mid, a, b));
    if(mid < b) ans = _up_(ans, _query(rson(t), mid + 1, e, a, b));
    return ans;
}

/// 几乎不用动
void _modify(int t, int s, int e, int a, int b, const lazy_type & delta) {
    if(a <= s and e <= b) {
        _dn_(t, s, e, delta);
        return;
    }
    _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    if(a <= mid) _modify(lson(t), s, mid, a, b, delta);
    if(mid < b) _modify(rson(t), mid + 1, e, a, b, delta);
    _pushUp(t);
    return;
}

/// 这个函数不用动
void _pushUp(int t) {
    data[t] = _up_(data[lson(t)], data[rson(t)]);
}

/// 这个函数几乎不用动
void _pushDown(int t, int s, int e) {
    if(lazy_zero() == lazy[t]) return;
    auto & lz = lazy[t];
    auto ls = lson(t), rs = rson(t);
    int mid = (s + e) >> 1;

    _dn_(ls, s, mid, lz);
    _dn_(rs, mid + 1, e, lz);

    lz = lazy_zero();
}

/// 这两个函数不用变动
static int lson(int x) {return x << 1;}
static int rson(int x) {return lson(x) | 1;}

int N;
/// 树结构, 1-index
vector<vector<int>> g;
/// 点权值
vector<llt> weight;

/// 建单向边
void mkDiEdge(int a, int b){
    g[a].push_back(b);
}
/// 建双向边
void mkBiEdge(int a, int b){
    mkDiEdge(a, b); mkDiEdge(b, a);
}

/// 树链剖分结构
struct node_t{
    int parent; // 父节点
    int hson;   // 重儿子
    int depth;  // 该节点的深度, 根节点深度为0
    int size;   // 本节点所领子树的节点总数
    int top;    // 本节点所在重链的顶
    int nid;    // 本节点在线段树中的编号, 即dfs序
    int mdes;   // 本节点所领子树的线段树编号均在[nid, mdes]中
};

int root; // 树根
vector<int> nid2old; // nid2old[i]表示线段树中第i个节点在原树中的编号
int timestamp; // 辅助变量
vector<node_t> nodes;

/// 递归找重边
void _dfsHeavyEdge(int u, int p, int d){
    auto & n = nodes[u];
    n.parent = p;
    n.depth = d;
    n.size = 1;

    for(auto v : g[u]){
        if(v == p) continue;
        _dfsHeavyEdge(v, u, d + 1);
        n.size += nodes[v].size;
        if(nodes[n.hson].size < nodes[v].size) n.hson = v;
    }
    return;
}

/// 递归找重链
void _dfsHeavyPath(int u, int top){
    auto & n = nodes[u];
    n.top = top;
    nid2old[n.mdes = n.nid = ++timestamp] = u;

    if(0 == n.hson) return;
    _dfsHeavyPath(n.hson, top);
    n.mdes = max(n.mdes, nodes[n.hson].mdes);

    for(auto v : g[u]){
        if(v != n.parent and v != n.hson){
            _dfsHeavyPath(v, v);
            n.mdes = max(n.mdes, nodes[v].mdes);
        }
    }
    return;
}
/// 递归建线段树
void _build(int t, int s, int e) {
    if(s == e) {
        data[t] = mkValue(weight[nid2old[s]]); // 注意线段树编号与原树编号存在转换
        return; 
    }
    int mid = (s + e) >> 1;
    _build(lson(t), s, mid);
    _build(rson(t), mid + 1, e);
    _pushUp(t);
}

/// 初始化, n是树的点数
void init(int n){
    N = n;
    timestamp = 0;
    /// 初始化树结构
    g.assign(N + 1, {});
    weight.assign(N + 1, 0);
    /// 初始化树链结构
    nodes.assign(N + 1, {0, 0, 0, 0, 0, 0, 0});
    nid2old.assign(N + 1, 0);
    /// 初始化线段树结构
    data.assign(N + 1 << 2, value_zero());
    lazy.assign(N + 1 << 2, lazy_zero());    
}

/// 在输入所有数据以后构建
void build(int root){
    /// 建树链
    _dfsHeavyEdge(this->root = root, 0, 0);
    _dfsHeavyPath(root, root);
    /// 建线段树
    _build(1, 1, N);
}

/// 求原树上x和y的LCA
int lca(int x, int y){
    while(nodes[x].top != nodes[y].top){
        if(nodes[nodes[x].top].depth < nodes[nodes[y].top].depth) y = nodes[nodes[y].top].parent;
        else x = nodes[nodes[x].top].parent;
    }
    return nodes[x].depth <= nodes[y].depth ? x : y;
}

/// 查询原树上x到y的路径信息
value_type query(int x, int y){
    value_type ans = value_zero();
    while(nodes[x].top != nodes[y].top){
        if(nodes[nodes[x].top].depth < nodes[nodes[y].top].depth) swap(x, y);

        ans = _up_(ans, _query(1, 1, N, nodes[nodes[x].top].nid, nodes[x].nid));
        x = nodes[nodes[x].top].parent;
    }
    if(nodes[x].depth > nodes[y].depth) swap(x, y);
    ans = _up_(ans, _query(1, 1, N, nodes[x].nid, nodes[y].nid));
    return ans;
}

/// 查询原树上x子树的信息
value_type query(int x){
    return _query(1, 1, N, nodes[x].nid, nodes[x].mdes);
}

/// 有序查询路径信息，如果需要维护诸如左右端信息，需要有序查询
value_type queryPath(int x, int y){
    vector<int> vx, vy;
    vx.emplace_back(x);
    vy.emplace_back(y);
    while(1){
        x = vx.back(); y = vy.back();
        if(nodes[x].top == nodes[y].top) break;

        if(nodes[nodes[x].top].depth < nodes[nodes[y].top].depth){
            y = nodes[nodes[y].top].parent;
            vy.emplace_back(y);
        }else{
            x = nodes[nodes[x].top].parent;
            vx.emplace_back(x);
        } 
    }

    auto ans = value_zero();
    for(int x,i=0,n=vx.size()-1;i<n;++i){
        x = vx[i];
        auto tmp = _query(1, 1, N, nodes[nodes[x].top].nid, nodes[x].nid);
        HLD::reverse(tmp);
        ans = _up_(ans, tmp);
    }

    x = vx.back();
    y = vy.back();
    if(nodes[x].depth <= nodes[y].depth){
        ans = _up_(ans, _query(1, 1, N, nodes[x].nid, nodes[y].nid));
    }else{
        auto tmp = _query(1, 1, N, nodes[y].nid, nodes[x].nid);
        HLD::reverse(tmp);
        ans = _up_(ans, tmp);
    }

    for(int i=vy.size()-2;i>=0;--i){
        y = vy[i];
        ans = _up_(ans, _query(1, 1, N, nodes[nodes[y].top].nid, nodes[y].nid));
    }

    return ans;
}

/// 原树上x到y的路径修改
void modify(int x, int y, const lazy_type & delta){
    while(nodes[x].top != nodes[y].top){
        if(nodes[nodes[x].top].depth < nodes[nodes[y].top].depth) swap(x, y);

        _modify(1, 1, N, nodes[nodes[x].top].nid, nodes[x].nid, delta);
        x = nodes[nodes[x].top].parent;
    }
    if(nodes[x].depth > nodes[y].depth) swap(x, y);
    _modify(1, 1, N, nodes[x].nid, nodes[y].nid, delta);
    return;
}

/// 原树上x子树的修改
void modify(int x, const lazy_type & delta){
    _modify(1, 1, N, nodes[x].nid, nodes[x].mdes, delta);
}

}St;




int main(){
#ifndef ONLINE_JUDGE
    // freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(0);   
    int n, q; cin >> n >> q;

    St.init(n);
    for(int i=1;i<=n;++i) St.weight[i] = i % 2;
    for(int u,v,i=1;i<n;++i){
        cin >> u >> v;
        St.mkBiEdge(u, v);
    }
    St.build(1);

    for(int c,u,v,d,i=0;i<q;++i){
        cin >> c >> u;
        // cout << i << ": " << c << endl;
        switch(c){
            case 1:{
                St.modify(u, 0x4);
                break;
            }
            case 2:{
                cin >> d;
                St.modify(u, 0x2 | d);
                break;
            }
            case 3:{
                cin >> v;
                St.modify(u, v, 0x4);
                break;
            }
            case 4:{
                cin >> v >> d;
                St.modify(u, v, 0x2 | d);
                break;
            }
            case 5:{
                cin >> v;
                auto ans = St.queryPath(u, v);
                cout << ans.count[1] << "\n";
                break;
            }
            case 6:{
                cin >> v;
                auto ans = St.queryPath(u, v);
                cout << ans.count[0] << "\n";
                break;
            }
            case 7:{
                cin >> v;
                auto ans = St.queryPath(u, v);
                cout << ans.longest[1] << "\n";
                break;
            }
            case 8:{
                cin >> v;
                auto ans = St.queryPath(u, v);
                cout << ans.longest[0] << "\n";
                break;
            }
            case 9:{
                cin >> v;
                auto ans = St.queryPath(u, v);
                cout << ans.duan << "\n";
                break;
            }
            default: assert(0);
        }
    }
    return 0;
}